package cn.jbt.api.admin.service.impl;

import cn.jbt.api.admin.entity.*;
import cn.jbt.api.admin.filter.LoginInfoAccessContext;
import cn.jbt.api.admin.repository.SysRoleMenuRepository;
import cn.jbt.api.admin.repository.SysRoleRepository;
import cn.jbt.api.admin.repository.SysUserRoleRepository;
import cn.jbt.api.admin.service.ISysRoleService;
import cn.jbt.common.PageQryParam;
import cn.jbt.common.PageQryResult;
import com.querydsl.core.types.ExpressionUtils;
import com.querydsl.core.types.Predicate;
import com.querydsl.jpa.JPAExpressions;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
@Slf4j
@AllArgsConstructor
public class SysRoleServiceImpl implements ISysRoleService {

    private final JPAQueryFactory jpaQueryFactory;
    private final SysRoleRepository sysRoleRepository;
    private final SysRoleMenuRepository sysRoleMenuRepository;
    private final SysUserRoleRepository sysUserRoleRepository;

    @Override
    public List<String> selectAllPermissionByUserId(long userId) {
        QSysRole qSysRole = QSysRole.sysRole;
        QSysUserRole qSysUserRole = QSysUserRole.sysUserRole;

        return jpaQueryFactory.selectDistinct(qSysRole.rolePermission)
                .from(qSysUserRole)
                .leftJoin(qSysRole).on(qSysUserRole.roleId.eq(qSysRole.id))
                .where(qSysUserRole.userId.eq(userId).and(qSysRole.status.eq(1)
                        .and(qSysRole.rolePermission.ne(""))))
                .fetch();
    }

    @Override
    public PageQryResult<SysRole> getAllByPage(PageQryParam<SysRole> pageQryParam) {
        SysRole qParam = pageQryParam.getParam();
        QSysRole qSysRole = QSysRole.sysRole;
        QSysRoleMenu qSysRoleMenu = QSysRoleMenu.sysRoleMenu;

        Predicate predicate = qSysRole.isNotNull().or(qSysRole.isNull());
        if (null != qParam) {
            if (!StringUtils.isEmpty(qParam.getRoleName())) {
                predicate = ExpressionUtils.and(predicate,
                        qSysRole.roleName.contains(qParam.getRoleName()));
            }
            if (!StringUtils.isEmpty(qParam.getRolePermission())) {
                predicate = ExpressionUtils.and(predicate,
                        qSysRole.rolePermission.contains(qParam.getRolePermission()));
            }
            if (null != qParam.getStatus()) {
                predicate = ExpressionUtils.and(predicate,
                        qSysRole.status.eq(qParam.getStatus()));
            }
        }

        List<SysRole> list = jpaQueryFactory.select(qSysRole)
                .from(qSysRole)
                .where(predicate).orderBy(qSysRole.id.desc())
                .offset((pageQryParam.getPage() - 1) * pageQryParam.getSize())
                .limit(pageQryParam.getSize())
                .fetch()
                .stream()
                .peek(sysRole -> sysRole.setMenuIds(jpaQueryFactory.select(qSysRoleMenu.menuId)
                        .from(qSysRoleMenu)
                        .where(qSysRoleMenu.roleId.eq(sysRole.getId()))
                        .fetch()
                ))
                .collect(Collectors.toList());

        long total = Optional.ofNullable(jpaQueryFactory.select(qSysRole.id.count())
                .from(qSysRole)
                .where(predicate)
                .fetchOne()
        ).orElse(0L);

        return new PageQryResult<>(list, total);
    }

    @Override
    public List<SysRole> getAll(SysRole sysRole) {
        QSysRole qSysRole = QSysRole.sysRole;

        Predicate predicate = qSysRole.isNotNull().or(qSysRole.isNull());
        if (null != sysRole) {
            if (!StringUtils.isEmpty(sysRole.getRoleName())) {
                predicate = ExpressionUtils.and(predicate,
                        qSysRole.roleName.contains(sysRole.getRoleName()));
            }
            if (!StringUtils.isEmpty(sysRole.getRolePermission())) {
                predicate = ExpressionUtils.and(predicate,
                        qSysRole.rolePermission.contains(sysRole.getRolePermission()));
            }
            if (null != sysRole.getStatus()) {
                predicate = ExpressionUtils.and(predicate,
                        qSysRole.status.eq(sysRole.getStatus()));
            }
        }

        return jpaQueryFactory.select(qSysRole)
                .from(qSysRole)
                .where(predicate).orderBy(qSysRole.id.desc())
                .fetch();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public SysRole add(SysRole sysRole) {
        if (sysRole.getRolePermission().equals("admin")) {
            throw new RuntimeException("角色权限已存在");
        }

        if (null != sysRoleRepository.findFirstByRolePermission(
                sysRole.getRolePermission())) {
            throw new RuntimeException("角色权限已存在");
        }

        sysRole.setCreateBy(LoginInfoAccessContext.getLoginInfo().getNickname());
        sysRole.setUpdateBy(sysRole.getCreateBy());
        sysRole = sysRoleRepository.save(sysRole);

        // 关联菜单
        if (CollectionUtils.isNotEmpty(sysRole.getMenuIds())) {
            List<SysRoleMenu> sysRoleMenus = new ArrayList<>();
            for (Long menuId : sysRole.getMenuIds()) {
                sysRoleMenus.add(new SysRoleMenu().setRoleId(sysRole.getId()).setMenuId(menuId));
            }
            sysRoleMenuRepository.saveAll(sysRoleMenus);
        }

        return sysRole;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public SysRole edit(SysRole sysRole) {
        SysRole newSysRole = sysRoleRepository.findFirstById(sysRole.getId());
        if (null == newSysRole) {
            throw new RuntimeException("404 NotFound");
        }

        if (null != sysRoleRepository.findFirstByRolePermissionAndIdNot(
                sysRole.getRolePermission(), newSysRole.getId())) {
            throw new RuntimeException("角色权限已存在");
        }

        newSysRole.edit(sysRole);
        newSysRole.setUpdateBy(LoginInfoAccessContext.getLoginInfo().getNickname());
        newSysRole = sysRoleRepository.save(newSysRole);

        // 删除菜单
        sysRoleMenuRepository.deleteByRoleId(newSysRole.getId());
        sysRoleMenuRepository.flush();

        // 关联菜单
        if (CollectionUtils.isNotEmpty(sysRole.getMenuIds())) {
            List<SysRoleMenu> sysRoleMenus = new ArrayList<>();
            for (Long menuId : sysRole.getMenuIds()) {
                sysRoleMenus.add(new SysRoleMenu().setRoleId(newSysRole.getId()).setMenuId(menuId));
            }
            sysRoleMenuRepository.saveAll(sysRoleMenus);
        }

        return newSysRole;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void del(List<Long> ids) {
        sysRoleRepository.deleteAllByIdInBatch(ids);
        sysRoleMenuRepository.deleteByRoleIdIn(ids);
        sysUserRoleRepository.deleteByRoleIdIn(ids);
    }

    @Override
    public List<String> getNamesByUserId(long userId) {
        QSysUserRole qSysUserRole = QSysUserRole.sysUserRole;
        QSysRole qSysRole = QSysRole.sysRole;

        return jpaQueryFactory.select(qSysRole.roleName)
                .from(qSysRole)
                .where(qSysRole.id.in(
                        JPAExpressions.select(qSysUserRole.roleId).from(qSysUserRole).where(qSysUserRole.userId.eq(userId))
                ))
                .orderBy(qSysRole.orderby.asc(), qSysRole.id.desc())
                .fetch();
    }

}
